module utils.GLSLFreeCam;
import maths.Vec;
import std.math;
import derelict.sdl.sdl;
import derelict.opengl.gl;
import derelict.opengl.glu;
import std.stdio;

import framework.JobHub;
import framework.Events;
import framework.GLSLRenderer;



class FreeCam{
	vec3 position;
	vec3 view;
	vec3 vector;
	vec3 strafe;
	
	this(float positionX, float positionY, float positionZ,
			 		    float viewX,     float viewY,     float viewZ,
						float upVectorX, float upVectorY, float upVectorZ){
		positionCamera(positionX, positionY, positionZ,
								viewX, viewY, viewZ,
								upVectorX, upVectorY, upVectorZ);
		
		eventPooler.register(&this.getMouseMotion);
		eventPooler.register(&this.getKey);
		renderer.register(&this.render);
		jobHub.register(&this.update);
		SDL_ShowCursor(SDL_DISABLE);
		strafe=vec3(-1,0,0);

	}

	void positionCamera(float positionX, float positionY, float positionZ,
			 		    float viewX,     float viewY,     float viewZ,
						float upVectorX, float upVectorY, float upVectorZ){
		position=vec3(positionX,positionY,positionZ);
		view=vec3(viewX,viewY,viewZ);
		vector=vec3(upVectorX,upVectorY,upVectorZ);
	}

	void setViewByMouse(SDL_MouseMotionEvent mousePos) {								
		int middleX = 800  >> 1;				
		int middleY = 600 >> 1;				
		float angleY = 0.0f;							
		float angleZ = 0.0f;							
		static float currentRotX = 0.0f;
		if( (mousePos.x == middleX) && (mousePos.y == middleY) ) return;
		angleY = cast(float)( (middleX - mousePos.x) ) / 500.0f;		
		angleZ = cast(float)( (middleY - mousePos.y) ) / 500.0f;		
		currentRotX -= angleZ;  
		if(currentRotX > 1.0f){	
			currentRotX = 1.0f;
			} else if(currentRotX < -1.0f)
				currentRotX = -1.0f;
			else {
				vec3 axis = vec3.cross(view - position, vector);
				axis.normalize;
				rotateView(angleZ, axis.x, axis.y, axis.z);
				rotateView(angleY, 0, 1, 0);
			}
		SDL_WarpMouse(middleX,middleY);
	}

	void rotateView(float angle, float x, float y, float z){
		vec3 newView;
		vec3 tmpview = view - position;		
		float cosTheta = cast(float)cos(angle);
		float sinTheta = cast(float)sin(angle);
		newView.x  = (cosTheta + (1 - cosTheta) * x * x)		* tmpview.x;
		newView.x += ((1 - cosTheta) * x * y - z * sinTheta)	* tmpview.y;
		newView.x += ((1 - cosTheta) * x * z + y * sinTheta)	* tmpview.z;
		newView.y  = ((1 - cosTheta) * x * y + z * sinTheta)	* tmpview.x;
		newView.y += (cosTheta + (1 - cosTheta) * y * y)		* tmpview.y;
		newView.y += ((1 - cosTheta) * y * z - x * sinTheta)	* tmpview.z;
		newView.z  = ((1 - cosTheta) * x * z - y * sinTheta)	* tmpview.x;
		newView.z += ((1 - cosTheta) * y * z + x * sinTheta)	* tmpview.y;
		newView.z += (cosTheta + (1 - cosTheta) * z * z)		* tmpview.z;
		view = position + newView;
	}

	void moveCamera(float speed,float sspeed)
	{
		vec3 vVector = view - position;
		vVector.normalize;
		
		vec3 st= vec3.cross(view,view+vec3(0,0.1,0));
		st.normalize;

		/*position.x += vVector.x * speed;		
		position.y += vVector.y * speed;		
		position.z += vVector.z * speed;		
		view.x += vVector.x * speed;			
		view.y += vVector.y * speed;			
		view.z += vVector.z * speed;	*/
		
		position += vVector * speed;
		view += vVector * speed;
		position += st * sspeed;
		view += st * sspeed;
		
		/*position.x += vVector.x * speed;		
		position.y += vVector.y * speed;		
		position.z += vVector.z * speed;		
		view.x += vVector.x * speed;			
		view.y += vVector.y * speed;			
		view.z += vVector.z * speed;	*/
	}

	void look()
	{
		gluLookAt(position.x, position.y, position.z,	
				  view.x,	view.y,     view.z,	
				  vector.x, vector.y, vector.z);
	}
	
	bool mouseMoved;
	SDL_MouseMotionEvent mouseEv;
	int direction;
	int streif;
	
	void update(){
		if(mouseMoved){				
			setViewByMouse(mouseEv);
			mouseMoved=false;
		}
		if(direction!=0||streif!=0){
				moveCamera(direction*0.01,streif*0.015);
		}
	}
	
	void render(){
		look;
		//writefln(position, view, vector);
	}
	
	void getMouseMotion(SDL_MouseMotionEvent e){
		mouseMoved=true; 
		mouseEv=e; 
	}
	
	void getKey(SDL_KeyboardEvent k){
		if(k.type==SDL_KEYDOWN){
			switch(k.keysym.sym){
				case SDLK_UP: case  SDLK_w: direction=1; break;
				case SDLK_DOWN: case  SDLK_s: direction=-1; break;
				case SDLK_LEFT: case  SDLK_a: streif=-1; break;
				case SDLK_RIGHT: case  SDLK_d: streif=1; break;
				default: break;
			}
			return;			
		} 
		if(k.type==SDL_KEYUP){
			switch(k.keysym.sym){
				case SDLK_UP: case  SDLK_w: direction=0; break;
				case SDLK_DOWN: case  SDLK_s: direction=0; break;
				case SDLK_LEFT: case  SDLK_a: streif=0; break;
				case SDLK_RIGHT: case  SDLK_d: streif=0; break;
				default: break;
			}
			return;
		}
		assert(false);
	}
}